package pers.xiaojun.boot.module.system.service.permission;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import pers.xiaojun.boot.common.exception.BusinessException;
import pers.xiaojun.boot.common.util.CollectionUtils;
import pers.xiaojun.boot.module.system.controller.permission.vo.role.RoleReqVO;
import pers.xiaojun.boot.module.system.controller.permission.vo.role.RoleRespVO;
import pers.xiaojun.boot.module.system.controller.permission.vo.role.RoleSaveVO;
import pers.xiaojun.boot.module.system.dao.mapper.permission.RoleMapper;
import pers.xiaojun.boot.module.system.dao.entity.permission.RoleDO;
import pers.xiaojun.boot.module.system.enums.permission.RoleCodeEnum;
import pers.xiaojun.boot.module.system.enums.permission.RoleTypeEnum;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import static pers.xiaojun.boot.module.system.constants.BusinessCodeConstants.*;
import static pers.xiaojun.boot.module.system.constants.RedisKeyConstants.Cache.SUPER_ADMIN_ROLE;

/**
 * 角色服务类
 *
 * @author xiaojun
 * @since 2025-10-10
 */
@Service
@RequiredArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RoleDO> implements RoleService {

    private final RoleMenuService roleMenuService;

    @Override
    public RoleDO saveRole(RoleSaveVO saveVO) {
        // 检查角色基础信息
        validateRoleBasicInfo(saveVO.getName(), saveVO.getCode(), null);

        // 写入
        RoleDO role = BeanUtil.toBean(saveVO, RoleDO.class)
                .setType(RoleTypeEnum.CUSTOM.getType()); // 默认自定义角色
        this.baseMapper.insert(role);
        return role;
    }

    @Override
    public RoleDO updateRole(RoleSaveVO updateVO) {
        // 检查是否可以更新
        validateRoleForUpdate(updateVO.getId());
        // 检查基础信息
        validateRoleBasicInfo(updateVO.getName(), updateVO.getCode(), updateVO.getId());

        RoleDO role = BeanUtil.toBean(updateVO, RoleDO.class);
        this.baseMapper.updateById(role);
        return role;
    }

    @Override
    public void removeRoleById(Long roleId) {
        // 校验角色是否可以删除
        validateRoleForUpdate(roleId);
        // 删除
        this.baseMapper.deleteById(roleId);
        // 删除角色拥有的菜单
        roleMenuService.removeByRoleId(roleId);
    }

    @Override
    public void removeRoleByIds(List<Long> roleIds) {
        for (Long id : roleIds) {
            validateRoleForUpdate(id);
        }
        this.baseMapper.deleteByIds(roleIds);
        // 删除角色拥有的菜单
        roleMenuService.removeByRoleIds(roleIds);
    }

    @Override
    public RoleRespVO getRoleVOById(Long roleId) {
        return BeanUtil.toBean(validateRoleExists(roleId), RoleRespVO.class);
    }

    @Override
    public Page<RoleDO> listRolePage(RoleReqVO roleReqVO) {
        return this.baseMapper.selectPage(roleReqVO);
    }

    @Override
    public List<RoleDO> listRolesByIds(List<Long> roleIds) {
        return this.baseMapper.selectByIds(roleIds);
    }

    @Override
    public boolean hasAnySuperAdmin(List<Long> roleIds) {
        RoleDO superAdminRole = getSelf().getRoleBySuperAdmin();
        if(superAdminRole == null) {
            return false;
        }
        return roleIds.contains(superAdminRole.getId());
    }

    @Override
    public boolean hasSuperAdmin(Long roleId) {
        return getSelf().getRoleBySuperAdmin().getId().equals(roleId);
    }

    @Override
    @Cacheable(value = SUPER_ADMIN_ROLE, key = "'super_admin'", unless = "#result == null")
    public RoleDO getRoleBySuperAdmin() {
        return this.baseMapper.selectOne(new LambdaQueryWrapper<RoleDO>()
                .eq(RoleDO::getCode, RoleCodeEnum.SUPER_ADMIN.getCode())
        );
    }

    /**
     * 校验角色是否存在
     *
     * @param id 角色Id
     * @return 角色
     */
    public RoleDO validateRoleExists(Long id) {
        RoleDO role = this.baseMapper.selectById(id);
        if (role == null) {
            throw new BusinessException(ROLE_NOT_EXISTS);
        }
        return role;
    }

    @Override
    public void validateRolesExists(Collection<Long> roleIds) {
        List<RoleDO> roleList = this.baseMapper.selectByIds(roleIds);

        if (roleList.size() != roleIds.size()) {
            Sets.SetView<RoleDO> notFindIds = Sets.difference(
                    new HashSet<>(roleList),
                    CollectionUtils.convertSet(roleList, RoleDO::getId)
            );
            throw new BusinessException(ROLE_NOT_EXISTS.getCode(), StrUtil.format("角色id {} 不存在", notFindIds));
        }
    }

    /**
     * 检查角色基础信息（名称、代码）
     *
     * @param name 角色名称
     * @param code 角色代码
     * @param id   角色Id
     */
    private void validateRoleBasicInfo(String name, String code, Long id) {
        // 检查代码是否为超级管理员
        if (RoleCodeEnum.isSuperAdmin(code)) {
            throw new BusinessException(ROLE_NOT_SET_SUPER_ADMIN);
        }
        // 检查名称是否存在
        RoleDO role = this.baseMapper.selectByName(name);
        if (role != null && !role.getId().equals(id)) {
            throw new BusinessException(ROLE_NAME_EXISTS);
        }
        // 检查代码是否存在
        role = this.baseMapper.selectByCode(code);
        if (role != null && !role.getId().equals(id)) {
            throw new BusinessException(ROLE_CODE_EXISTS);
        }
    }

    /**
     * 更新/删除校验
     *
     * @param id 角色Id
     */
    private RoleDO validateRoleForUpdate(Long id) {
        // 校验角色是否存在
        RoleDO role = validateRoleExists(id);
        // 内置角色无法操作
        if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) {
            throw new BusinessException(ROLE_SYSTEM_NOT_OPERABLE);
        }
        return role;
    }


    /**
     * 获取注册在Bean容器中当前类，解决无法调用本身的 @Cacheable 方法（AOP）
     */
    private RoleService getSelf() {
        return SpringUtil.getBean(this.getClass());
    }
}
